package main


import "fmt"
import ."api"
import "io/ioutil"
import "state"
import ."util"


func main() {

	data, err := ioutil.ReadFile("test.luac")
	if err != nil {
		panic(err)
	}

	ls := state.New()
	ls.Register("print", print)		// 注冊print函數
	ls.Register("getmetatable", getMetatable)
	ls.Register("setmetatable", setMetatable)
	ls.Register("next", next)
	ls.Register("pairs", pairs)
	ls.Register("ipairs", ipairs)

	ls.Load(data, "chunk", "b")
	ls.Call(0, 0)

}


func print(ls LuaState) int {

	nArgs := ls.GetTop()  // 此时栈里存放的都是参数

	for i := 1; i <= nArgs; i++ {

		if ls.IsBoolean(i) {

			fmt.Printf("%t", ls.ToBoolean(i))

		} else if ls.IsString(i) {

			fmt.Print(ls.ToString(i))

		} else {

			fmt.Print(ls.TypeName(ls.Type(i)))

		}

		if i < nArgs {
			fmt.Printf("\t")
		}

	}

	fmt.Println()

	return 0

}


// 有且仅有一个参数，所以传入索引1即可
// 如果值有元表，方法结束后元表已经在栈顶了
// 否则需要把nil值推入栈顶
// 最后返回1，把栈顶值(元表或nil)返回给Lua函数
func getMetatable(ls LuaState) int {

	PrintStack(ls)

	if !ls.GetMetatable(1) {
		ls.PushNil()
	}

	return 1

}


func setMetatable(ls LuaState) int {

	ls.SetMetatable(1)

	return 1

}


// 因为next()函数的第二个参数(键)是可选的
// 所以首先调用SetTop()方法以便在用户没有提供该参数时给它补上缺省值nil
// 这样就能保证栈里肯定有两个值，索引1处是表，索引2处是上一个键
// 然后我们通过索引1调用Next()方法，他会把键从栈顶弹出
// 如果Next()方法返回true，说明遍历还没有结束，下一个键值对已经在栈顶了，我们返回2即可
// 否则，遍历已经结束，我们需要自己往栈顶推入nil，然后返回1
func next(ls LuaState) int {

	ls.SetTop(2)	// 如果参数2不存在则设置为nil

	if ls.Next(1) {

		return 2
	} else {

		ls.PushNil()
		return 1
	}

}


// pairs()函数实际上就是返回了next函数(对应_f)、表(对应_s)以及nil(对应_var)这三个值而已
func pairs(ls LuaState) int {

	ls.PushGoFunction(next)		// will return generator

	ls.PushValue(1)				// state, 

	ls.PushNil()

	return 3
}


func ipairs(ls LuaState) int {

	ls.PushGoFunction(_iPairsAux)  // iteration function
	ls.PushValue(1)					// state
	ls.PushInteger(0)				// initial value

	return 3
}


func _iPairsAux(ls LuaState) int {

	i := ls.ToInteger(2) + 1
	ls.PushInteger(i)

	if ls.GetI(1, i) == LUA_TNIL {
		return 1
	} else {
		return 2
	}
}